#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdbool.h>
#include <libgen.h>
#include "bl_crc32.h"
#include "package.h"

#define NUBIN_VERSION "1.1.0"

char *outfile = "update.lub"; /* 输出文件的文件名 */

typedef struct {
    uint32_t magic_numb;
    uint32_t tbl_ver;        // [31:24] major, [23:12] minor [11:0] reversion
    char model[8];
    uint16_t flag;
    uint16_t block_size;
    uint32_t build_ts;
    uint32_t ctx_crc;
    uint32_t raw_size;
    uint32_t ctx_size;
    char parti_name[12];
    char reserve1[16];
    char reserve2[12];
    uint32_t header_crc;
}bl_header_info, *bl_header_info_t;

typedef struct _ddr2buf
{
    unsigned int addr;
    unsigned int val;
}DDR2Buf;

typedef struct _spi_flash_header
{
    unsigned int ftyp;
    unsigned int reseved0;
    unsigned int app_sz;
    unsigned int reseved1;
    unsigned int AA55;
    unsigned int ddr2_sz;
    DDR2Buf buf[55];   // ddr2_sz + 1
}AppHeader;

AppHeader appHeader = {
    .ftyp = 0x4E565420,
    .reseved0 = 0,
    .app_sz = 0,
    .reseved1 = 0xFFFFFFFF,
    .AA55= 0xAA55AA55,
    .ddr2_sz = 54,
    .buf = {
    {0xB0000220, 0x01000000},
    {0xB0000264, 0xC0000018},
    {0xB0000220, 0x01000018},
    {0x55AA55AA, 0x1},
    {0x55AA55AA, 0x1},
    {0xB0001828, 0x53DCD84A},
    {0xB0001808, 0x00008014},
    {0x55AA55AA, 0x1},
    {0x55AA55AA, 0x1},
    {0x55AA55AA, 0x1},
    {0xB0001800, 0x00030476},
    {0x55AA55AA, 0x1},
    {0xB0001804, 0x00000021},
    {0x55AA55AA, 0x1},
    {0xB0001804, 0x00000023},
    {0x55AA55AA, 0x1},
    {0x55AA55AA, 0x1},
    {0x55AA55AA, 0x1},
    {0xB0001804, 0x00000027},
    {0x55AA55AA, 0x1},
    {0x55AA55AA, 0x1},
    {0x55AA55AA, 0x1},
    {0xB0001820, 0x00000000},
    {0xB0001824, 0x00000000},
    {0xB000181C, 0x00004000},
    {0xB0001818, 0x00000332},
    {0xB0001810, 0x00000006},
    {0xB0001804, 0x00000027},
    {0x55AA55AA, 0x1},
    {0x55AA55AA, 0x1},
    {0x55AA55AA, 0x1},
    {0xB0001804, 0x0000002B},
    {0xB0001804, 0x0000002B},
    {0xB0001804, 0x0000002B},
    {0xB0001818, 0x00000232},
    {0xB000181C, 0x00004781},
    {0xB000181C, 0x00004401},
    {0xB0001804, 0x00000020},
    {0xB0001834, 0x00888820},
    {0x55AA55AA, 0x1},
    {0x55AA55AA, 0x1},
    {0x55AA55AA, 0x1},
    {0xB0000218, 0x00000008},
    {0xB80030A0, 0x00007FFF},
    {0xB80030E0, 0x0000FF00},
    {0xB8003120, 0x0000FFFC},
    {0xB8003160, 0x0000F800},
    {0xB80031A0, 0x0000803C},
    {0xB80031E0, 0x0000FF7C},
    {0xB8003220, 0x00000001},
    {0xB000022C, 0x00000100},
    {0xB000022C, 0x00000100},
    {0xB000022C, 0x00000100},
    {0xB000022C, 0x00000100},

    {0x0, 0x0},
    },
};

static void printUsage(const char *appname)
{
    char *name, *bname;
    name = strdup(appname);
    bname = basename(name);
    printf( "Version: " NUBIN_VERSION "\r\n"
            "Usage: %s OPTIONS\r\n"
            "Make lub from bin\r\n"
            "\r\n"
            "OPTIONS:\r\n"
            "    --model || -m <instrument model string>    8max\r\n"
            "    --partition || -p <flash partition>        12 bytes max\r\n"
            "    -i <src bin>                               Input bin file\r\n"
            "    [-o <out path>]                            Output file, update.lub default\r\n"
            , bname);
    free(name);
}

int lub_archive(int argc, char *argv[])
{
    int arg_c = argc-1;
    char **arg_v = &argv[1];
    char *model = NULL, *partition = NULL, *ifile = NULL, *ofile = NULL;

    if(argc < 2){
        printUsage(argv[0]);
        return 0;
    }

    while(arg_c > 0){
        if(strcmp(arg_v[0], "--model") == 0 || strcmp(arg_v[0], "-m") == 0){
            model = arg_v[1];
        }else if(strcmp(arg_v[0], "--partition") == 0 || strcmp(arg_v[0], "-p") == 0){
            partition = arg_v[1];
        }else if(strcmp(arg_v[0], "-i") == 0){
            ifile = arg_v[1];
        }else if(strcmp(arg_v[0], "-o") == 0){
            ofile = arg_v[1];
        }else{
        }
        arg_c--; arg_v++;
    }
    if (((model == NULL) || (strlen(model) > 8)) ||
        ((partition == NULL) || (strlen(partition) > 12)) ||
        (ifile == NULL)) {
        printUsage(argv[0]);
        return 0;
    }

    if (ofile != NULL) {
        outfile = ofile;
    }

    FILE *ifp, *ofp;
    struct stat st;
    bl_header_info header;
    size_t raw_size = 0;
    size_t ctx_size = 0;
    uint8_t *ctx_buf = NULL;
    size_t rd;

    if(stat(ifile, &st) < 0){
        perror("Stat file ");
        return -1;
    }
    raw_size = st.st_size;
printf("raw size: %08X %d\n", raw_size, raw_size);
    ifp = fopen(ifile, "rb");
    if(!ifp){
        printf("Open file %s failure!\n", ifile);
        return -1;
    }
    ofp = fopen(outfile, "wb+");
    if(!ofp){
        printf("Open file %s failure!\n", outfile);
        fclose(ifp);
        return -1;
    }

    header.magic_numb = IDENTIFIER;
    header.tbl_ver = SOFTWARE_VERSION;
    strncpy(header.model, model, 8);
    header.block_size = BLOCK_SIZE;
    strncpy(header.parti_name, partition, 12);

    //time(&tim);
    header.build_ts = st.st_ctime;//tim;
    header.raw_size = raw_size;
    appHeader.app_sz = raw_size;

    ctx_buf = malloc(4 * 1024 * 1024);
    memcpy(ctx_buf, &appHeader, sizeof(AppHeader));
    ctx_size = sizeof(AppHeader);

    while (1) {
        rd = fread(&ctx_buf[ctx_size], 1, header.block_size, ifp);
        if (rd <= 0) break;
        ctx_size += rd;
    }

    // context crc & size
    header.ctx_crc = bl_crc32sum(ctx_buf, ctx_size);
    header.ctx_size = ctx_size;
printf("ctx size: %08X %d\n", ctx_size, ctx_size);
printf("ctx  crc: %08X\n", header.ctx_crc);
    // header crc
    header.header_crc = bl_crc32sum(&header, sizeof(bl_header_info)-4);
printf("hdr  crc: %08X\n", header.header_crc);
    // write out file
    fwrite(&header, sizeof(bl_header_info), 1, ofp);
    fwrite(ctx_buf, ctx_size, 1, ofp);

_err_exit:
    if (ctx_buf) free(ctx_buf);

    fclose(ofp);
    fclose(ifp);

    return 0;
}
